<?php

namespace app\common\controller;

use think\App;
use think\Controller;
use think\Db;

/**
 * 项目公共控制器
 * @package app\common\controller
 */
class Fypay extends Controller
{

    public function __construct(App $app = null)
    {
        $this->xml = new \XMLWriter();
        parent::__construct($app);
    }

    /*
     * 条码支付（商户扫用户二维码收款）
     * $order 订单信息
     * order.order_type 订单类型：ALIPAY(支付宝) WECHAT(微信) UNIONPAY(银联二维码) BESTPAY(翼支付) DIGICCY(数字货币)
     * order.goods_des 商品名称, 显示在用户账单的商品、商品说明等地方
     * order.mchnt_order_no 商户订单号
     * order.order_amt 总金额, 订单总金额，单位为分
     * order.term_ip 实时交易终端IP
     * order.txn_begin_ts 交易起始时间, 订单生成时间，格式为yyyyMMddHHmmss
     * order.auth_code 扫码支付授权码，设备读取用户的条码或者二维码信息
     *
     */
    public function ActivePay($order, $pemstr, $shopconf)
    {
        $url = 'https://fundwx.fuiou.com/micropay';
        $data = [
            'version' => $shopconf['version'],
            'ins_cd' => $shopconf['ins_cd'],
            'mchnt_cd' => $shopconf['mchnt_cd'],
            'term_id' => $shopconf['term_id'],
            'random_str' => $shopconf['random_str'],
            'order_type' => $order['order_type'],
            'goods_des' => $order['goods_des'],
            'goods_tag' => '',//商品标记，可空
            'goods_detail' => '',//单品优惠功能字段,可空
            'addn_inf' => '',//附加数据,可空
            'mchnt_order_no' => $order['mchnt_order_no'],//商户订单号, 商户系统内部的订单号
            'curr_type' => 'CNY', //货币类型,默认人民币：CNY,可空
            'order_amt' => $order['order_amt'],
            'term_ip' => $order['term_ip'],
            'txn_begin_ts' => $order['txn_begin_ts'],//交易起始时间, 订单生成时间
            'auth_code' => $order['auth_code'],
            'sence' => '1', //支付场景,默认1；1: 条码支付2: 声波支付 3: 刷脸支付
        ];
        //签名处理
        $data['sign'] = $this->make_sign($data, $pemstr);
        $data['reserved_fy_term_id'] = $shopconf['reserved_fy_term_id'];
        //完整的xml格式
        $xml = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><xml>" . $this->toXml($data) . "</xml>";

        //经过两次urlencode()之后的字符串
        $con = "req=" . urlencode(urlencode($xml));
        //返回的xml字符串
        $resultXml = URLdecode($this->http_post($url, $con));
        //dump($resultXml);
        //将xml转化成对象
        $ob = $this->toArray($resultXml);
        return $ob;
    }

    /**
     * 主扫统一下单（用户扫商户动态二维码）
     * $order 订单信息
     * order.order_type 订单类型：ALIPAY(支付宝) WECHAT(微信) UNIONPAY(银联二维码) BESTPAY(翼支付) DIGICCY(数字货币)
     * order.goods_des 商品名称, 显示在用户账单的商品、商品说明等地方
     * order.mchnt_order_no 商户订单号
     * order.order_amt 总金额, 订单总金额，单位为分
     * order.term_ip 实时交易终端IP
     * order.txn_begin_ts 交易起始时间, 订单生成时间，格式为yyyyMMddHHmmss
     * order.notify_url 订单回调地址
     * @return array
     */
    public function PassivePay($order, $pemstr, $shopconf)
    {
        $url = 'https://fundwx.fuiou.com/preCreate';
        $data = [
            'version' => $shopconf['version'],
            'ins_cd' => $shopconf['ins_cd'],
            'mchnt_cd' => $shopconf['mchnt_cd'],
            'term_id' => $shopconf['term_id'],
            'random_str' => $shopconf['random_str'],
            'order_type' => $order['order_type'],
            'notify_url' => $order['notify_url'],
            'goods_des' => $order['goods_des'],
            'goods_tag' => '',//商品标记，可空
            'goods_detail' => '',//单品优惠功能字段,可空
            'addn_inf' => '',//附加数据,可空
            'mchnt_order_no' => $order['mchnt_order_no'],
            'curr_type' => 'CNY', //货币类型,默认人民币：CNY,可空
            'order_amt' => $order['order_amt'],//总金额, 订单总金额，单位为分
            'term_ip' => $order['term_ip'],
            'txn_begin_ts' => $order['txn_begin_ts'],
        ];
        //签名处理
        $data['sign'] = $this->make_sign($data, $pemstr);

        //完整的xml格式
        $xml = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><xml>" . $this->toXml($data) . "</xml>";

        //经过两次urlencode()之后的字符串
        $con = "req=" . urlencode(urlencode($xml));
        //返回的xml字符串
        $resultXml = URLdecode($this->http_post($url, $con));
        //将xml转化成对象
        $ob = $this->toArray($resultXml);
        return $ob;
    }

    /*
     * 订单查询
     * $order 订单信息
     * $pemstr 签名文件
     * $shopconf 商户参数配置
     */
    public function OrderQuery($order, $pemstr, $shopconf)
    {
        $url = 'https://fundwx.fuiou.com/commonQuery';
        $data = [
            'version' => $shopconf['version'],
            'ins_cd' => $shopconf['ins_cd'],
            'mchnt_cd' => $shopconf['mchnt_cd'],
            'term_id' => $shopconf['term_id'],
            'random_str' => $shopconf['random_str'],
            'order_type' => $order['order_type'],
            'mchnt_order_no' => $order['mchnt_order_no'],
        ];
        //签名处理
        $data['sign'] = $this->make_sign($data, $pemstr);
        //完整的xml格式
        $xml = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><xml>" . $this->toXml($data) . "</xml>";

        //经过两次urlencode()之后的字符串
        $con = "req=" . urlencode(urlencode($xml));
        //返回的xml字符串
        $resultXml = URLdecode($this->http_post($url, $con));
        //将xml转化成对象
        $ob = $this->toArray($resultXml);
        $errormsg = [
            'SUCCESS' => '成功',
            'USERPAYING' => '支付中',
            'NOTPAY' => '未支付',
            'CLOSED' => '已关闭',
            'REVOKED' => '已撤销',
            'REFUND' => '已退款'
        ];
        if ($ob['result_code'] == '000000') {
            $ob['trans_stat_msg'] = $errormsg[$ob['trans_stat']];
        } elseif ($ob['result_code'] == '9999') {
            $ob['trans_stat_msg'] = '系统错误，需要继续查询';
        } elseif ($ob['result_code'] == '1010') {
            $ob['trans_stat_msg'] = '找不到交易';
        } else {
            $ob['trans_stat_msg'] = '支付失败';
        }
        return $ob;
    }

    /*
     * 订单退款
     * $order 订单信息
     * $pemstr 签名文件
     * $shopconf 商户参数配置
     */
    public function OrderRefund($order, $pemstr, $shopconf)
    {
        $url = 'https://fundwx.fuiou.com/commonRefund';
        $data = [
            'version' => $shopconf['version'],
            'ins_cd' => $shopconf['ins_cd'],
            'mchnt_cd' => $shopconf['mchnt_cd'],
            'term_id' => $shopconf['term_id'],
            'random_str' => $shopconf['random_str'],
            'order_type' => $order['order_type'],
            'refund_order_no' => $order['refund_order_no'],
            'mchnt_order_no' => $order['mchnt_order_no'],
            'total_amt' => $order['total_amt'],
            'refund_amt' => $order['refund_amt'],
            'operator_id' => $order['operator_id'],
        ];
        //签名处理
        $data['sign'] = $this->make_sign($data, $pemstr);
        //完整的xml格式
        $xml = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><xml>" . $this->toXml($data) . "</xml>";

        //经过两次urlencode()之后的字符串
        $con = "req=" . urlencode(urlencode($xml));
        //返回的xml字符串
        $resultXml = URLdecode($this->http_post($url, $con));
        //将xml转化成对象
        $ob = $this->toArray($resultXml);
        return $ob;
    }

    /*
     * 退款查询
     * $order 订单信息
     * $pemstr 签名文件
     * $shopconf 商户参数配置
     */
    public function RefundQuery($order, $pemstr, $shopconf)
    {
        $url = 'https://fundwx.fuiou.com/refundQuery';
        $data = [
            'version' => $shopconf['version'],
            'ins_cd' => $shopconf['ins_cd'],
            'mchnt_cd' => $shopconf['mchnt_cd'],
            'term_id' => $shopconf['term_id'],
            'random_str' => $shopconf['random_str'],
            'refund_order_no' => $order['refund_order_no'],
        ];
        //签名处理
        $data['sign'] = $this->make_sign($data, $pemstr);
        //完整的xml格式
        $xml = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><xml>" . $this->toXml($data) . "</xml>";

        //经过两次urlencode()之后的字符串
        $con = "req=" . urlencode(urlencode($xml));
        //返回的xml字符串
        $resultXml = URLdecode($this->http_post($url, $con));
        //将xml转化成对象
        $ob = $this->toArray($resultXml);
        $errormsg = [
            'SUCCESS' => '退款成功',
            'PAYERROR' => '退款失败',
        ];
        if ($ob['result_code'] == '000000') {
            $ob['trans_stat_msg'] = $errormsg[$ob['trans_stat']];
        } else {
            $ob['trans_stat_msg'] = '系统错误，需要继续查询';
        }
        return $ob;
    }


    /*
     * 生成签名
     */
    public function make_sign($data, $pem)
    {
        //对数组进行排序
        ksort($data);
        $str = '';
        foreach ($data as $k => $v) {
            $str .= $k . '=' . $v . '&';
        }
        $str = rtrim($str, "&");
        //dump($str);
        //获取私钥
        $pkeyid = openssl_pkey_get_private($pem);
        //dump($pkeyid);
        //MD5WithRSA私钥加密
        openssl_sign($str, $sign, $pkeyid, OPENSSL_ALGO_MD5);
        //返回base64加密之后的数据
        $t = base64_encode($sign);
        //解密-1:error验证错误 1:correct验证成功 0:incorrect验证失败
        //$pubkey = openssl_pkey_get_public($pem);
        //$ok = openssl_verify($str,base64_decode($t),$pubkey,OPENSSL_ALGO_MD5);
        //var_dump($ok);
        return $t;

    }

    /*
     * 数组转xml
     */
    private function toXml($data, $eIsArray = FALSE)
    {
        if (!$eIsArray) {
            $this->xml->openMemory();
        }
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $this->xml->startElement($key);
                $this->toXml($value, TRUE);
                $this->xml->endElement();
                continue;
            }
            $this->xml->writeElement($key, $value);
        }
        if (!$eIsArray) {
            $this->xml->endElement();
            return $this->xml->outputMemory(true);
        }
    }

    /**
     * 将xml转为array
     * @param string $xml xml字符串
     * @return array       转换得到的数组
     */
    public function toArray($xml)
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $result;
    }

    /*
     * 发送请求
     */
    private function http_post($url, $data)
    {
        //对空格进行转义
        $url = str_replace(' ', '+', $url);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_VERBOSE, 0);
        $output = curl_exec($ch);
        $errorCode = curl_errno($ch);
        //释放curl句柄
        curl_close($ch);
        if (0 !== $errorCode) {
            return false;
        }
        return $output;
    }
}